#ifndef _RSTACK_H_
#define _RSTACK_H_

#include "Any.h"
#include "RModule.h"
#include "Environment.h"

namespace R {
#define STACK_SIZE 0x1000
class Environment;
class EnvInfo;

	class IntStack {
			uint64_t stack[STACK_SIZE];
			uint64_t top;

		public:
			IntStack() { top = 0; }

			void push(uint64_t val);
			uint64_t pop(uint64_t n=1);
			uint64_t peek(uint64_t n=0);
			void set(uint64_t idx, uint64_t val);

			void dup() {
				push(peek());
			}

			uint64_t size() {
				return top;
			}
	};

	class AnyStack {
			Any*     stackAny[STACK_SIZE + 2];
			uint64_t top;

		public:
			AnyStack() {
				top = 0;
			}

			void push(Any* x, bool safe = false);
			void JITPush(Any* x) __attribute__((noinline));
			Any* JITPop(uint64_t n) __attribute__((noinline));
			Any* JITPeek(uint64_t n) __attribute__((noinline));
			Any* pop(uint64_t n = 1);
			Any* peek(uint64_t n = 0);
			void set(uint64_t idx, Any* x);

			void swap();

			uint64_t size();

			void dump();

			template <class T>
			T* pop(uint64_t n = 1){
					R_VAL(Any*, res, pop(n));
					Assert(Any::isOfType<T>(res));
					return static_cast<T*>(res);
			}

			template <class T>
			T* peek(uint64_t n = 0){
					R_VAL(Any*, res, peek(n));
					Assert(Any::isOfType<T>(res));
					return static_cast<T*>(res);
			}

			void dup() {
				R_VAL(Any*, tmp, peek());
				push(tmp);
			};

			// This method is here temporary for CALLBUILTIN op
			Any **asStorage(uint64_t n){
				return stackAny + top - n;
			}

			void tracer(word_t closure);
	};

	class ActivationRecord {
		public:
			RModule *module;
			uint64_t pc;
			EnvInfo* envInfo;
			ActivationRecord(){ module = NULL; pc = -1; envInfo = NULL;}
			void set(RModule *mod, uint64_t _pc, EnvInfo* _envInfo){
				module = mod;
				pc = _pc;
				envInfo = _envInfo;
			}
	};

	class CallStack {
			ActivationRecord stack[STACK_SIZE];
			uint64_t top;

		public:
			CallStack() { top = 0; }

			void push(RModule *mod, uint64_t pc, EnvInfo* envIdx);
			ActivationRecord& pop(uint64_t n=1);
			ActivationRecord* peek(uint64_t n);

			uint64_t size() {
				return top;
			}
	};

	class EnvStack {
			// EmbeddedEnvs inlineStack[STACK_SIZE];
			EnvInfo* stack;
			uint64_t top;

		public:

			EnvStack();
			~EnvStack();
			/*
			 * Push a GC environment and returns the corresponding EnvInfo.
			 */
			EnvInfo* pushTopLevel(Environment* env);

			/*
			 * Create a new environment with given enclosing environment.
			 * Return the index of newly created environment.
			 */
			EnvInfo* push(EnvInfo* parentInfo, FunInfo* info, bool isGc = true);

			/*
			 * Create a new environment with given parent an info.
			 * Return the index of newly created environment.
			 */
			EnvInfo* push(Environment* parentEnv, FunInfo* info, bool isGc = true);

			/*
			 * Pop an environment, returns the poped index.
			 */
			EnvInfo* pop(uint64_t n=1);

			/*
			 * Returns current environment's index.
			 */
			EnvInfo* peek(uint64_t n=0);

			uint64_t size() {
				return top;
			}
			void tracer(word_t closure);

	};

	class Stack {
		public:
			IntStack intStack;
			AnyStack anyStack;
			CallStack callStack;
			EnvStack envStack;

			void tracer(word_t closure);
	};

};
#endif
